#pragma once

#include <unistd.h>
#include <string>
#include <cstdarg>
#include <fstream>
#include <cstring>
#include "LockGuard.hpp"

namespace log_ns
{
    enum
    {
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    std::string GetCurTime()
    {
        time_t now = time(nullptr); // 获取当前的时间戳

        // 转换
        struct tm *cur_time = localtime(&now);
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d",
                 cur_time->tm_year + 1900,
                 cur_time->tm_mon + 1,
                 cur_time->tm_mday,
                 cur_time->tm_hour,
                 cur_time->tm_min,
                 cur_time->tm_sec);

        return buffer;
    }

    std::string LevelToString(int level)
    {
        switch (level)
        {
        case DEBUG:
            return "DEBUG";
        case INFO:
            return "INFO";
        case WARNING:
            return "WARNING";
        case ERROR:
            return "ERROR";
        case FATAL:
            return "FATAL";
        default:
            return "UNKNOWN";
        }
    }
    class logmessage
    {
    public:
        std::string _level;
        pid_t _id;
        std::string _filename;
        int _filenumber;
        std::string _cur_time;
        std::string _msg_info;
    };

#define SCREEN_TYPE 1
#define FILE_TYPE 2

    const std::string glogfile = "./log.txt";
    pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;

    class Log
    {
    public:
        Log(const std::string &logfile = glogfile)
            : _type(SCREEN_TYPE) // 默认向屏幕输出
              ,
              _logfile(logfile)
        {
        }
        ~Log() {}

        void Enable(int type)
        {
            _type = type;
        }

        void FlushLogToSCREEN(const logmessage &lg)
        {
            // [等级][pid][filename][filenumber][time] 日志内容
            printf("[%s][%d][%s][%d][%s] %s",
                   lg._level.c_str(),
                   lg._id,
                   lg._filename.c_str(),
                   lg._filenumber,
                   lg._cur_time.c_str(),
                   lg._msg_info.c_str());
        }

        void FlushLogToFILE(const logmessage &lg)
        {
            std::ofstream out(_logfile, std::ios::app); // 追加模式，防止新数据覆盖旧数据
            if (!out.is_open())
                return; // 打开出错

            char buffer[128];
            snprintf(buffer, sizeof(buffer), "[%s][%d][%s][%d][%s] %s",
                     lg._level.c_str(),
                     lg._id,
                     lg._filename.c_str(),
                     lg._filenumber,
                     lg._cur_time.c_str(),
                     lg._msg_info.c_str());

            out.write(buffer, strlen(buffer));
            out.close();
        }

        void FlushLog(const logmessage &lg)
        {
            LockGuard lockguard(&glock);
            // 根据模式判断，应该刷新到显示器还是文件
            if (_type == SCREEN_TYPE)
                FlushLogToSCREEN(lg);
            else
                FlushLogToFILE(lg);
        }
        // 调用此接口，输出日志信息
        void logMessage(const std::string &filename, int filenumber, int level, const char *format, ...)
        {
            logmessage lg;

            lg._level = LevelToString(level);
            lg._id = getpid();
            lg._filename = filename;
            lg._filenumber = filenumber;

            // 获取当前时间
            lg._cur_time = GetCurTime();

            // 获取日志内容
            va_list ap;
            va_start(ap, format); // 初始化ap，指向可变参数
            char buffer[1024];
            vsnprintf(buffer, sizeof(buffer), format, ap); // 使用 format 格式输出，ap 可以找到参数
            va_end(ap);
            lg._msg_info = buffer;

            // 刷新
            FlushLog(lg);
        }

    private:
        int _type;
        std::string _logfile;
    };

    Log lg;

#define LOG(Level, Format, ...)                                          \
    do                                                                   \
    {                                                                    \
        lg.logMessage(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__); \
    } while (0)

#define ENABLE_SCREEN()         \
    do                          \
    {                           \
        lg.Enable(SCREEN_TYPE); \
    } while (0);

#define ENABLE_FILE()         \
    do                        \
    {                         \
        lg.Enable(FILE_TYPE); \
    } while (0);
}
